package io.flash.rpc.demo;

import com.alibaba.fastjson.JSON;
import io.flash.rpc.client.Exchanges;
import io.flash.rpc.client.NettyClient;
import io.flash.rpc.protocol.RpcRequestPacket;
import io.flash.rpc.protocol.RpcResponsePacket;
import io.flash.rpc.server.NettyServer;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@SuppressWarnings("unchecked")
public class ServiceFactory {
    private static final Map<String, Object> serviceMap = new ConcurrentHashMap<>();
    private static final Map<String, Method> methodMap = new ConcurrentHashMap<>();

    private static NettyClient nettyClient = new NettyClient();

    private static NettyServer nettyServer = new NettyServer();

    static {
        nettyServer.start();

        nettyClient.start();
    }


    public static <T> T getService(Class<T> serviceClass) {
        return (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{serviceClass}, (proxy, method, args) -> {
            // 1. 构造调用请求数据包
            RpcRequestPacket rpcRequestPacket = new RpcRequestPacket(serviceClass, method, args);

            // 2. 将请求缓存到本地
            Exchanges.save(rpcRequestPacket);

            // 3. 将请求发送到服务端
            nettyClient.send(rpcRequestPacket);

            // 4. 等待服务端返回
            rpcRequestPacket.waitResponse();

            // 5. 拿到响应数据包
            RpcResponsePacket rpcResponsePacket = Exchanges.getResponse(rpcRequestPacket);

            // 6. 解码成方法对应的对象
            return JSON.parseObject(rpcResponsePacket.getReturnValue(), method.getReturnType());
        });
    }

    public static <T> void registerService(Class<T> serviceClass, Object service) {
        serviceMap.put(serviceClass.getName(), service);

        addAllMethod(serviceClass);
    }

    private static <T> void addAllMethod(Class<T> serviceClass) {
        for (Method method : serviceClass.getDeclaredMethods()) {
            addMethod(serviceClass, method);
        }
    }

    private static <T> void addMethod(Class<T> serviceClass, Method method) {

        methodMap.put(getFullMethodName(serviceClass.getName(), method.getName()), method);
    }


    public static Object getService(String serviceClassName) {

        return serviceMap.get(serviceClassName);
    }

    public static Method getMethod(RpcRequestPacket rpcRequestPacket) {


        return methodMap.get(getFullMethodName(rpcRequestPacket.getServiceClassName(), rpcRequestPacket.getMethodName()));
    }

    private static String getFullMethodName(String serviceClassName, String methodName) {

        return serviceClassName + "#" + methodName;
    }
}
